﻿using SoundEngine3.AudioProcessing;
using SoundEngine3.DspPlugins;
using SoundEngine3.MIDI.Meta;
using System;
using System.Collections.Generic;
using System.Text;
using ZquenceStudio3.Core;
using ZquenceStudio3.Core.DspPlugins;
using ZquenceStudio3.DspPlugins;
using ZquenceStudio3.Core.Storage;
using SharpDepend.Storage;

namespace ZquenceStudio3Host.DspPlugins.Synthesizers
{
    public class LayerDescription : IDspPlugin
    {
        public DspPluginInfo GetDspPluginInfo()
        {
            return new DspPluginInfo("Layer", DspType.Synthesizer);
        }

        public BaseDspPluginInstance CreateDsp(ZquenceStudio3Context context, DspPluginInfo info)
        {
            return new Layer(context, info);
        }
    }

    public class Layer : CoreDspSynthesizerPlugin
    {
        internal class LayerMap
        {
            private List<MidiMessage> mProcessedMessages = new List<MidiMessage>();

            public SoundEngine3.SequenceContent.Track Synthesizer;
            public int NoteStart;
            public int NoteEnd;
            public int OctaveOffset;

            internal bool IsInRange(byte note)
            {
                return note >= NoteStart && note <= NoteEnd;
            }

            internal void AddMessage(ref MidiMessage message)
            {
                if (OctaveOffset != 0)
                {
                    var moddedMessage = message;
                    moddedMessage.MidiData.Parameter1 = (byte)Math.Max(0, Math.Min(120, moddedMessage.MidiData.Parameter1 + (OctaveOffset * 12)));
                    mProcessedMessages.Add(moddedMessage);
                }
                else
                {
                    mProcessedMessages.Add(message);
                }
            }

            internal void Process()
            {
                Synthesizer.DspSynthesizer.ProcessEvents(mProcessedMessages);
                mProcessedMessages.Clear();
            }
        }

        private Dictionary<SoundEngine3.SequenceContent.Track, LayerMap> mLayerMaps = new Dictionary<SoundEngine3.SequenceContent.Track, LayerMap>();
        private CustomNode mData;

        internal IDictionary<SoundEngine3.SequenceContent.Track, LayerMap> LayerMaps { get { return mLayerMaps; } }

        public Layer(ZquenceStudio3Context application, DspPluginInfo dspPlugin)
            : base(application, dspPlugin)
        {
            SupportAudioProcessing = false;
        }

        protected override void UnpackData(CustomNode node)
        {
            if (CoreContext.LoadingState == LoadingState.Loading)
            {
                mData = node;
            }
            else
            {
                ParseData(node);
            }
        }

        protected override void PackData(CustomNode node)
        {
            StringBuilder sb = new StringBuilder();
            int i = 0;
            foreach (var map in mLayerMaps)
            {
                int tackId = map.Value.Synthesizer.ID;
                int start = map.Value.NoteStart;
                int end = map.Value.NoteEnd;
                int offset = map.Value.OctaveOffset;

                sb.Append(tackId);
                sb.Append(":");
                sb.Append(start);
                sb.Append(":");
                sb.Append(end);
                sb.Append(":");
                sb.Append(offset);

                node.AddProperty("map", sb.ToString());

                sb.Clear();
            }

            base.PackData(node);
        }
        
        public override void OnLoaded()
        {
            if (mData != null)
            {
                ParseData(mData);
                mData = null;
            }
        }

        private void ParseData(CustomNode data)
        {
            var maps = data.Properties;

            foreach (var map in maps)
            {
                string[] mapdata = map.Value.Split(':');
                int trackId = int.Parse(mapdata[0]);
                int start = int.Parse(mapdata[1]);
                int end = int.Parse(mapdata[2]);
                int offset = int.Parse(mapdata[3]);
                
                var track = CoreContext.SoundEngine.Sequencer.FindTrack(trackId);

                if (track == null)
                {
                    AddError(string.Format("Synthesizer {0} not found to map.", trackId));
                }
                else
                {
                    LayerMap layer = new LayerMap();
                    layer.Synthesizer = track;
                    layer.NoteStart = start;
                    layer.NoteEnd = end;
                    layer.OctaveOffset = offset;

                    mLayerMaps.Add(track, layer);
                }
            }

            OnParsedData?.Invoke();
        }

        public event Action OnParsedData;

        public override string PluginInfo
        {
            get { return DspPluginInfo.DefaultPluginInfo; }
        }

        public override Version Version
        {
            get { return DspPluginInfo.DefaultVersion; }
        }
        
        public override void OnProcess(BaseAudioBuffer output)
        {
            throw new NotImplementedException();
        }

        protected override void OnProcessEvents(List<MidiMessage> midiMessages)
        {
            if (midiMessages.Count > 0)
            {
                foreach (var map in mLayerMaps)
                {
                    for (int i = 0; i < midiMessages.Count; i++)
                    {
                        MidiMessage message = midiMessages[i];

                        if (map.Value.IsInRange(message.MidiData.Parameter1))
                        {
                            map.Value.AddMessage(ref message);
                        }
                    }

                    map.Value.Process();
                }
            }
        }
    }
}
